package moteur;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.Random;

import écrans.ConstantesDuJeu;
import écrans.TableVertePingPong;

/**
 * Cette classe est un récepteur de souris et de clavier. Elle calcule les
 * déplacements de la balle et des raquettes et change leurs coordonnées.
 */
public class MoteurJeuPingPong implements Runnable, MouseMotionListener,
		KeyListener, ConstantesDuJeu {

	private static final int ANGLE_BALLE_MAX = 5;
	private TableVertePingPong table; // Référence à la table.
	private int raquetteEnfant_Y = RAQUETTE_ENFANT_Y_DEPART;
	private int raquetteOrdinateur_Y = RAQUETTE_ORDINATEUR_Y_DEPART;
	private int scoreEnfant;
	private int scoreOrdinateur;
	private int balle_X; // position X de la balle
	private int balle_Y; // position Y de la balle
	private boolean déplacementGauche = true;
	private volatile boolean balleServie = false;

	// Valeur en pixels du déplacement vertical de la balle.
	private volatile int déplacementVertical;

	// Constructeur. Stocke une référence à la table.
	public MoteurJeuPingPong(TableVertePingPong tableVerte) {
		table = tableVerte;
		Thread travailleur = new Thread(this);
		travailleur.start();
	}

	// Méthodes requises par l'interface MouseMotionListener
	// (certaines sont vides, mais doivent être incluses dans
	// la classe de toute façon).

	public void mouseDragged(MouseEvent événement) {
	}

	public void mouseMoved(MouseEvent événement) {

		int souris_Y = événement.getY();

		// Si la souris est au-dessus de la raquette de l'enfant
		// et que la raquette n'a pas dépassé le haut de la
		// table, la déplace vers le haut ;
		// sinon, la déplace vers le bas.
		if (souris_Y < raquetteEnfant_Y && raquetteEnfant_Y > HAUT_TABLE) {
			raquetteEnfant_Y -= INCREMENT_RAQUETTE;
		} else if (raquetteEnfant_Y < BAS_TABLE) {
			raquetteEnfant_Y += INCREMENT_RAQUETTE;
		}

		// Affecte la nouvelle position de la raquette
		// table.positionnerRaquetteEnfant_Y(raquetteEnfant_Y);
	}

	// Méthodes requises par l'interface KeyListener.
	public void keyPressed(KeyEvent événement) {
		char touche = événement.getKeyChar();
		if ('n' == touche || 'N' == touche) {
			démarrerNouvellePartie();
		} else if ('q' == touche || 'Q' == touche) {
			terminerJeu();
		} else if ('s' == touche || 'S' == touche) {
			serviceEnfant();
		} else if (('p' == touche || 'P' == touche) && raquetteEnfant_Y > HAUT_TABLE)
			raquetteEnfant_Y -= INCREMENT_RAQUETTE;
		else if (('m' == touche || 'M' == touche) && raquetteEnfant_Y < BAS_TABLE )
			raquetteEnfant_Y += INCREMENT_RAQUETTE;
		table.positionnerRaquetteEnfant_Y(raquetteEnfant_Y);
	}

	public void keyReleased(KeyEvent événement) {
	}

	public void keyTyped(KeyEvent événement) {
	}

	// Démarre une nouvelle partie.
	public void démarrerNouvellePartie() {
		scoreOrdinateur = 0;
		scoreEnfant = 0;
		table.affecterTexteMessage("Scores - Ordinateur : 0" + "Enfant : 0");
		serviceEnfant();
	}

	// Termine le jeu.
	public void terminerJeu() {
		System.exit(0);
	}

	// La méthode run() est requise par l'interface Runnable.

	public void run() {

		boolean rebondPossible = false;
		while (true) {

			if (balleServie) {
				// Si la balle est en mouvement
				// Etape 1. La balle se déplace-t-elle vers la
				// gauche ?
				if (déplacementGauche && balle_X > BALLE_X_MIN) {
					rebondPossible = (balle_Y >= raquetteOrdinateur_Y && balle_Y < (raquetteOrdinateur_Y + LONGUEUR_RAQUETTE) ? true : false);
					balle_X -= INCREMENT_BALLE;

					// Ajoute un déplacement vertical à chaque
					// mouvement horizontal de la balle.
					balle_Y -= déplacementVertical;

					table.positionnerBalle(balle_X, balle_Y);
					// La balle peut-elle rebondir ?
					if (balle_X <= RAQUETTE_ORDINATEUR_X && rebondPossible) {
						déplacementGauche = false;
						déplacementVertical = new Random().nextInt(ANGLE_BALLE_MAX);
					}
				}

				// Etape 2. La balle se déplace-t-elle vers la
				// droite ?
				if (!déplacementGauche && balle_X <= BALLE_X_MAX) {
					rebondPossible = (balle_Y >= raquetteEnfant_Y
							&& balle_Y < (raquetteEnfant_Y + LONGUEUR_RAQUETTE) ? true
							: false);

					balle_X += INCREMENT_BALLE;
					balle_Y -= déplacementVertical;

					table.positionnerBalle(balle_X, balle_Y);
					// La balle peut-elle rebondir ?
					if (balle_X >= RAQUETTE_ENFANT_X && rebondPossible) {
						déplacementGauche = true;
						déplacementVertical = new Random().nextInt(ANGLE_BALLE_MAX)*-1;
					}
				}

				// rebond sur les bords du haut et du bas
				if (balle_Y > BALLE_Y_MAX || balle_Y < BALLE_Y_MIN)
					déplacementVertical = -déplacementVertical;

				// Etape 3. Déplace la raquette de l'ordinateur vers le
				// haut ou vers le bas pour bloquer la balle.

				if (!rebondPossible && raquetteOrdinateur_Y < balle_Y && raquetteOrdinateur_Y < BAS_TABLE) {
					raquetteOrdinateur_Y += new Random().nextInt(INCREMENT_RAQUETTE);
				} else if (!rebondPossible && raquetteOrdinateur_Y > HAUT_TABLE) {
					raquetteOrdinateur_Y -= new Random().nextInt(INCREMENT_RAQUETTE);
				}
				table.positionnerRaquetteOrdinateur_Y(raquetteOrdinateur_Y);

				// Etape 4. Sommeiller un peu
				try {
					Thread.sleep(DUREE_SOMMEIL);
				} catch (InterruptedException exception) {
					exception.printStackTrace();
				}

				// Etape 5. Mettre le score à jour si la balle est
				// dans la surface verte mais ne bouge plus.
				if (balleSurLaTable()) {
					if (balle_X > BALLE_X_MAX) {
						scoreOrdinateur++;
						afficherScore();
					} else if (balle_X < BALLE_X_MIN) {
						scoreEnfant++;
						afficherScore();
					}
				}
			} // Fin du if balleServie
		} // Fin du while
	}// Fin de run()

	// Sert depuis la position courante de la raquette
	// de l'enfant.
	private void serviceEnfant() {

		balleServie = true;
		balle_X = RAQUETTE_ENFANT_X - RAYON_BALLE;
		balle_Y = raquetteEnfant_Y;

		if (balle_Y > HAUTEUR_TABLE / 2) {
			déplacementVertical = new Random().nextInt(ANGLE_BALLE_MAX) * -1;
		} else {
			déplacementVertical = new Random().nextInt(ANGLE_BALLE_MAX);
		}

		table.positionnerBalle(balle_X, balle_Y);
		table.positionnerRaquetteEnfant_Y(raquetteEnfant_Y);
	}

	private void afficherScore() {
		balleServie = false;

		if (scoreOrdinateur == SCORE_GAGNANT) {
			table.affecterTexteMessage("L'ordinateur a gagné ! "
					+ scoreOrdinateur + " : " + scoreEnfant);
		} else if (scoreEnfant == SCORE_GAGNANT) {
			table.affecterTexteMessage("Tu as gagné ! " + scoreEnfant + " : "
					+ scoreOrdinateur);
		} else {
			table.affecterTexteMessage("Ordinateur : " + scoreOrdinateur
					+ " Enfant: " + scoreEnfant);
		}
	}

	// Vérifie que la balle n'a pas dépassé la limite
	// inférieure ou supérieure de la table.
	private boolean balleSurLaTable() {
		if (balle_Y >= BALLE_Y_MIN && balle_Y <= BALLE_Y_MAX) {
			return true;
		} else {
			return false;
		}
	}
}
